
import { Action } from './Action'
import { SingleParticle } from './SingleParticle'
import { any, must } from '../core/logic'
import { Vector3 } from '../core/math/Vector3'
import { ShapeType } from "./ParticleType";
import { PositionShape, VelocityShape } from "./shape";
import { PointsGeometry } from "../geom/PointsGeometry";
import { PointMaterial } from '../material/PointMaterial';
import { Mesh } from '../component/Mesh';
import { Object3D } from '../entity/Object3D';
import { Color } from '../core/math/Color';
import { Engine } from '../Engine';

/**
 * 粒子发射器
 */
export class Emitter {
    constructor(params) {
        this.positionShape = must(params.positionShape)
        this.velocityShape = must(params.velocityShape)
        this.acceleration = any(params.acceleration, new Vector3(0, 0, 0))
        this.angle = any(params.angle, 0)
        this.angleRange = any(params.angleRange, 360)
        this.angleVelocity = any(params.angleVelocity, 0)
        this.texturePath = must(params.texturePath)
        this.size = any(params.size, 1)
        this.color = any(params.color, new Vector3(1.0, 1.0, 1.0))
        this.opacity = any(params.opacity, 1)
        this.particlesPerSecond = any(params.particlesPerSecond, 100)
        this.duration = any(params.duration, 3)
        this.emitterAge = any(params.emitterAge, 60)
        this.loop = any(params.loop, true)

        this.setParameters()
        this.createParticles()
    }
    createParticles() {
        const count = Math.round(this._particleCount)
        this. positionArray = new Float32Array(count * 3)
        this. colorArray = new Float32Array(count * 3)
        this. sizeArray = new Float32Array(count)
        this. angleArray = new Float32Array(count)
        this. opacityArray = new Float32Array(count)
        this. visibleArray = new Float32Array(count)

        for (let i = 0; i < count; i++) {
            const particle = this.createParticle()
            this. positionArray[i * 3] = particle.position.x
            this.positionArray[i * 3 + 1] = particle.position.y
            this. positionArray[i * 3 + 2] = particle.position.z
            this.colorArray[i * 3] = particle._color.r
            this.colorArray[i * 3 + 1] = particle._color.g
            this.colorArray[i * 3 + 2] = particle._color.b
            this.sizeArray[i] = particle._size
            this.angleArray[i] = particle.angle
            this.opacityArray[i] = particle._opacity
            this. visibleArray[i] = particle.alive
            this._particles[i] = particle
        }
      let  blend = {
            alphaSrcFactor: "src-alpha",
            alphaDstFactor: "one-minus-src-alpha",
            alphaOperation: "add",
            colorSrcFactor: "src-alpha",
            colorDstFactor: "one-minus-src-alpha",
            colorOperation: "add",
          };
        this._geometry = new PointsGeometry({ position:  this.positionArray, rotation:  this.angleArray, color:  this.colorArray, size:  this.sizeArray, opacity:  this.opacityArray, visible:  this.visibleArray })
        this._material = new PointMaterial({ 
            map: this.texturePath ,       
            alphaCutoff: 0.5,
            alphaSrcFactor: blend?.alphaSrcFactor,
            alphaDstFactor: blend?.alphaDstFactor,
            alphaOperation: blend?.alphaOperation,
            colorSrcFactor: blend?.colorSrcFactor,
            colorDstFactor: blend?.colorDstFactor,
            colorOperation: blend?.colorOperation,})
        this._mesh = new Mesh({ geometry: this._geometry, material: this._material })
        this._object3D = new Object3D({ position: [0, 0, 0], rotation: [0, 0, 0] });
        this._object3D.addComponent(this._mesh);
        Engine.instance.scene.addEntity(this._object3D)
    }

    setParameters() {
        this._particles = []
        this._age = 0.0
        this._alive = true
        this._particleCount = this.particlesPerSecond * Math.min(this.duration, this.emitterAge)
    }
    randomValue(min, max) {
        return min + max * (Math.random() - 0.5)
    }
    randomVector3(min, max) {
        const rand3 = new Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)
        return new Vector3().addVectors(min, new Vector3().multiplyVectors(max, rand3))
    }
    createParticle() {
        const particle = new SingleParticle()
        if (this.positionShape.type == ShapeType.Cube) {
            particle.position = this.randomVector3(this.positionShape.position, this.positionShape.maxRange)
        }
        if (this.positionShape.type == ShapeType.Sphere) {
            const z = 2 * Math.random() - 1
            const t = Math.PI * 2 * Math.random()
            const r = Math.sqrt(1 - z * z)
            const vec3 = new Vector3(r * Math.cos(t), r * Math.sin(t), z)
            particle.position = new Vector3().addVectors(this.positionShape.position, vec3.multiplyScalar(this.positionShape.positionRadius))
        }
        if (this.positionShape.type == ShapeType.Circle) {
            const angle = Math.random() * 2 * Math.PI;
            const radius = Math.random() * this.positionShape.positionRadius;
            particle.position = new Vector3(
                this.positionShape.position.x + radius * Math.cos(angle),
                this.positionShape.position.y + radius * Math.sin(angle),
                this.positionShape.position.z // 圆形平面的z坐标保持不变
            );
        }
        if (this.positionShape.type == ShapeType.Cone) {
            const height = Math.random() * this.positionShape.coneHeight;
            const radius = (Math.random() * this.positionShape.coneBaseRadius) * (1 - height / this.positionShape.coneHeight);
            const angle = Math.random() * 2 * Math.PI;

            const x = radius * Math.cos(angle);
            const y = height;
            const z = radius * Math.sin(angle);

            particle.position = new Vector3(x, y, z).add(this.positionShape.position);
        }
        if (this.positionShape.type == ShapeType.Hemisphere) {
            const z = Math.sqrt(1 - (Math.random() * 2 - 1) ** 2); // 限制在半球范围内
            const t = Math.PI * 2 * Math.random();
            const r = Math.sqrt(1 - z * z);
            const vec3 = new Vector3(r * Math.cos(t), r * Math.sin(t), z);
            particle.position = new Vector3().addVectors(this.positionShape.position, vec3.multiplyScalar(this.positionShape.positionRadius));
        }
        if (this.velocityShape.type == ShapeType.Cube) {
            particle.velocity = this.randomVector3(this.velocityShape.velocity, this.velocityShape.velocityMax)
        }

        if (this.velocityShape.type == ShapeType.Sphere) {
            const direction = new Vector3().subVectors(particle.position, this.positionShape.position)
            const speed = this.randomValue(this.velocityShape.velocity, this.velocityShape.velocityMax)
            particle.velocity = direction.normalize().multiplyScalar(speed)
        }
        if (this.velocityShape.type == ShapeType.Circle) {
            const angle = Math.random() * 2 * Math.PI;
            const speed = this.randomValue(this.velocityShape.velocity, this.velocityShape.velocityMax);
            particle.velocity = new Vector3(
                speed * Math.cos(angle),
                speed * Math.sin(angle),
                0 // 圆形平面的z分量速度设为0
            );
        }
        if (this.velocityShape.type == ShapeType.Cone) {
            const heightRatio = Math.random();
            const baseRadius = this.velocityShape.coneBaseRadius * Math.sqrt(heightRatio);
            const angle = Math.random() * 2 * Math.PI;
            const speed = this.randomValue(this.velocityShape.velocity, this.velocityShape.velocityMax);

            const x = speed * baseRadius * Math.cos(angle);
            const y = speed * heightRatio * this.velocityShape.coneHeight;
            const z = speed * baseRadius * Math.sin(angle);

            particle.velocity = new Vector3(x, y, z);
        }
        if (this.velocityShape.type == ShapeType.Hemisphere) {
            const phi = Math.acos(1 - 2 * Math.random()); // 高度角，限制在半球内
            const theta = Math.random() * 2 * Math.PI; // 方位角
            const speed = this.randomValue(this.velocityShape.velocity, this.velocityShape.velocityMax);

            const x = speed * Math.sin(phi) * Math.cos(theta);
            const y = speed * Math.sin(phi) * Math.sin(theta);
            const z = speed * Math.cos(phi);

            particle.velocity = new Vector3(x, y, z);
        }

        particle.acceleration = this.acceleration
        particle.angle = this.randomValue(this.angle, this.angleRange)
        particle.angleVelocity = this.angleVelocity
        if (this.size instanceof Action) {
            particle.size = this.size
            particle._size=this.size.values[0]
        } else {
            particle._size = this.size
        }
        particle.size = this.size
        if (this.color instanceof Action) {
            particle.color = this.color
            particle._color= new Color().setRGB(this.color.values[0][0], this.color.values[0][1], this.color.values[0][2])
        } else {
            particle._color = new Color().setRGB(this.color[0], this.color[1], this.color[2])
        }
        if (this.opacity instanceof Action) {
            particle.opacity = this.opacity
            particle._opacity=this.opacity.values[0]
        } else {
            particle._opacity = this.opacity
        }
        particle.age = 0

        return particle
    }

    update(dt) {
        // return
        const recycleIndices = []
    // console.log(this.opacityArray,"this.opacityArray");
    // console.log(this.visibleArray,"this.visibleArray");
    // console.log(this.sizeArray,"this.sizeArray");
        for (let i = 0; i < this._particleCount; i++) {
            const particle = this._particles[i]
            if (particle.alive) {
             
                particle.update(dt)
                if (particle.age > this.duration) {
                    particle.alive = 0.0
                    recycleIndices.push(i)
                }
                this.positionArray[i * 3] = particle.position.x
                this.positionArray[i * 3 + 1] = particle.position.y
                this.positionArray[i * 3 + 2] = particle.position.z
                this.colorArray[i * 3] = particle.color.r
                this.colorArray[i * 3 + 1] = particle.color.g
                this.colorArray[i * 3 + 2] = particle.color.b
                this.visibleArray[i] = particle.alive
                this.opacityArray[i] = particle.opacity
                this.angleArray[i] = particle.angle
                this.sizeArray[i] = particle.size
            }
        }


        if (!this._alive) return

        if (this._age < this.duration) {
            let startIndex = Math.round(this.particlesPerSecond * (this._age + 0))
            let endIndex = Math.round(this.particlesPerSecond * (this._age + dt))
            if (endIndex > this._particleCount) {
                endIndex = this._particleCount
            }
            for (let i = startIndex; i < endIndex; i++) {
                this._particles[i].alive = 1.0
            }
        }

        for (let j = 0; j < recycleIndices.length; j++) {
            let i = recycleIndices[j]
            this._particles[i] = this.createParticle()
            this._particles[i].alive = 1.0
            this.positionArray[i * 3] = this._particles[i].position.x
            this.positionArray[i * 3 + 1] = this._particles[i].position.y
            this.positionArray[i * 3 + 2] = this._particles[i].position.z
        }
        this._geometry.position =  this.positionArray

        this._age += dt

        if (this._age > this.emitterAge && !this.loop) {
            this._alive = false
        }
    }
}
